package com.game.commons.scripts.java.impl.javacompiler;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import javax.tools.JavaFileObject;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;

import com.game.commons.scripts.java.ScriptClassLoader;
import com.game.commons.utils.ClassUtils;


/**
 * 脚本类加载器
 * @author 杜祥
 * @create 2013-6-21
 */
public class ScriptClassLoaderImpl extends ScriptClassLoader
{
	private static final Logger log = Logger.getLogger(ScriptClassLoaderImpl.class);
	
	
	private final ClassFileManager	classFileManager;
	
	/**
	 * 创建一个新的脚本类加载器，并设置类文件管理
	 * 
	 * @param classFileManager
	 *            这个类加载器的文件管理类
	 */
	ScriptClassLoaderImpl(ClassFileManager classFileManager)
	{
		super(new URL[] {});
		this.classFileManager = classFileManager;
	}

	/**
	 * 创建一个新的脚本类加载器，并设置类文件管理和父加载器
	 * 
	 * @param classFileManager
	 *           这个类加载器的文件管理类
	 * @param parent
	 *            父加载器
	 */
	ScriptClassLoaderImpl(ClassFileManager classFileManager, ClassLoader parent)
	{
		super(new URL[] {}, parent);
		this.classFileManager = classFileManager;
	}
	

	public ClassFileManager getClassFileManager()
	{
		return classFileManager;
	}

	/**
	 * 返回已完成编译的类名。
	 * @return 返回类名列表（此列表不可修改）
	 */
	@Override
	public Set<String> getCompiledClasses() 
	{
		Set<String> sets = classFileManager.getCompiledClasses().keySet();
		return Collections.unmodifiableSet(sets);
	}

	
	/**
	 * 获得指定类名的
	 */
	@Override
	public byte[] getByteCode(String className)
	{
		BinaryClass binary = classFileManager.getCompiledClasses().get(className);
		
		byte[] _byte = new byte[binary.getBytes().length];
		
		System.arraycopy(binary.getBytes(), 0, _byte, 0, _byte.length);
		
		return _byte;
	}
	
	
	/**
	 * 返回指定包名的类成员列表
	 * @param packageName	
	 * 						包名
	 * @return
	 * 				返回类成员列表
	 * @throws IOException 
	 * 				加载指定的包失败，抛出此异常。
	 */
	@SuppressWarnings("resource")
	public Set<JavaFileObject> getClassesForPackage(String packageName) throws IOException
	{
		Set<JavaFileObject> result = new HashSet<JavaFileObject>();
		
		ClassLoader parent = getParent();
		
		if(parent instanceof ScriptClassLoaderImpl)
		{
			ScriptClassLoaderImpl pscl = (ScriptClassLoaderImpl)parent;
			
			result.addAll(pscl.getClassesForPackage(packageName));
		}
		
		for(String cn : classFileManager.getCompiledClasses().keySet())
		{
			if(ClassUtils.isPackageMember(cn, packageName))
			{
				BinaryClass binary = classFileManager.getCompiledClasses().get(cn);
				
				result.add(binary);
			}
		}
		
		for(String cn : libraryClasses)
		{
			if(ClassUtils.isPackageMember(cn, packageName))
			{
				BinaryClass binary = new BinaryClass(cn);
				
				try 
				{
					byte[] data = getRawClassByName(cn);
					OutputStream os = binary.openOutputStream();
					os.write(data);
				} 
				catch (IOException e)
				{
					log.error(packageName + "包中的类加载出错！", e);
					throw e;
				}
				
				result.add(binary);
			}
		}
		return result;
	}
	
	
	/**
	 * 查找具有指定名称的类，获得类的二进制。
	 * @param name
	 * 				类名
	 * @return		
	 * @throws IOException
	 * 				加载类数据出错抛出此异常
	 */
	protected byte[] getRawClassByName(String name) throws IOException
	{
		URL resource = findResource(name.replace('.', '/').concat(".class"));
		InputStream is = null;
		byte[] clazz = null;
		
		try 
		{
			is = resource.openStream();
			clazz = IOUtils.toByteArray(is);
		}
		catch (IOException e)
		{
			log.error("加载类" + name + "的数据错误", e);
			throw e;
		}
		finally
		{
			if(is != null)
			{
				try
				{
					is.close();
				}
				catch (Exception e2)
				{
					log.error(name + "关闭流时出错！", e2);
				}
			}
		}
		return clazz;
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public Class<?> getDefinedClass(String name) 
	{
		BinaryClass bc = classFileManager.getCompiledClasses().get(name);
		if(bc == null)
		{
			return null;
		}
		return bc.getDefinedClass() ;
	}

	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setDefinedClass(String name, Class<?> clazz)throws IllegalArgumentException
	{
		BinaryClass bc = classFileManager.getCompiledClasses().get(name);

		if(bc == null)
		{
			throw new IllegalArgumentException("设置的类在编译时没有定义。");
		}
		bc.setDefinedClass(clazz);
	}

}
